
package com.hlkj.pay.app.merchant.ext.impl.dffpay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.date.DatePattern;
import com.alibaba.fastjson.JSON;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.constant.DffPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.dto.req.CallbackRequest;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.dto.req.CallbackTradeRequest;
import com.hlkj.pay.app.merchant.ext.impl.dffpay.utils.DffPayBase64;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.MerchantUtils;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author HlpayTeam
 * @date 2024/10/15 18:16
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_DFF_PAY)
public class DffPayMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    @Value("${hlkj.pay.openapi.checkSign:true}")
    private boolean checkSign;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String dffPay = getPayType(payWayDO);
        if (!StringUtils.hasText(dffPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(DffScenePayOrderExtPt.class, BizScenario.valueOf(dffPay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));

    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(DffScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(DffScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(DffScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return false;
        }
        return extensionExecutor.execute(DffScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.refundApply(channelRefundPayOrderContext));
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(DffScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));

    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(DffScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest servletRequest, ProviderChannelConfigDO providerChannelConfigDO) {
        String requestContext = null;
        try {
            // 获取完整的 JSON 字符串
            requestContext = params(servletRequest);
        }
        catch (Exception e) {
            log.error("从当前线程中获取参数失败 从流中重新获取失败:{}", e.getMessage(),e);
            return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
        }
        CallbackRequest obj = JSON.parseObject(requestContext, CallbackRequest.class);

        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
//        DffPayConfig dffPayConfig = getPayConfig(channelConfig, channelRefundPayOrderContext.getMerchantAppChannelDO());
//        DffPayConfig dffPayConfig = JsonUtils.parseObject(channelConfig, DffPayConfig.class);

        log.info("东方付支付回调参数:{}", requestContext);
        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = processCommonCall(obj);
        boolean verifySign = true;
        if (payOrderDOCommonResult.getData() != null)
            payOrderDOCommonResult.getData().setCallBackBody(requestContext);
        if (!verifySign && checkSign) {
            log.error("东方付签名错误:{}", "验签失败");
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            return payOrderDOCommonResult;
        }
        return payOrderDOCommonResult;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest callBackBody, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest callBackBody, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return getPayType(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        CommonEnum.COMMON_APP_TYPE commonAppType = null;
        switch (PayEnums.PAY_TYPE.fromByCode(payWayDO.getType())) {
            case WECHAT:
                switch (scenePayType) {
                    case QR:
                    case JSAPI:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.WECHAT_PUB;
                        break;
                    // case MINI_APP:
                    case H5:
                    case APP:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.WECHAT_MIN;
                        break;
                }
                break;
            case ALIPAY:
                switch (scenePayType) {
                    case JSAPI:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.ALIPAY_PUB;
                        break;
                    case MINI_APP:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.ALIPAY_MIN;
                        break;
                }
                break;
        }
        if (commonAppType == null) {
            return null;
        }

        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderContext.getProviderChannelConfigDO();
        switch (commonAppType) {
            case WECHAT_PUB:
                return providerChannelConfigDO.getWechatDefaultPubId();
            case WECHAT_MIN:
                return providerChannelConfigDO.getWechatDefaultMiniId();
            case ALIPAY_PUB:
                return providerChannelConfigDO.getAlipayPubId();
            case ALIPAY_MIN:
                return providerChannelConfigDO.getAlipayMiniId();
        }
        return null;
    }

    @Override
    public String callBackResponse(CommonResult commonResult) {
        String rspStr = "";
        if (!commonResult.isSuccess()) {
            rspStr = "FAIL";
        }
        return rspStr;
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if (extra != null && !CommonEnum.YES_NO_TYPE.NO.getCode().equals(extra.getOriginalType())) {
            return false;
        }
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        // 只有微信 app h5才需要
        switch (payType) {
            case WECHAT:
                break;
            default:
                return false;
        }
        switch (scenePayType) {
            case APP:
            case H5:
                break;
            default:
                return false;
        }
        return true;
    }

    /**
     * 汇付支付方式路由
     *
     * @param payWayDO
     * @return
     */
    private String getPayType(PayWayDO payWayDO) {
        log.info("东方付实际支付type:{}", payWayDO.getSubType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        switch (scenePayType) {
//            case QR:
//            case JSAPI:
            case H5:
            case MINI_APP:
            case APP:
            case WAP:
            case PC:
                return DffPayExtConstant.PAY;
            default:
                return null;
        }
    }

    /**
     * 通用回调参数解析
     *
     * @param obj
     * @return
     */
    CommonResult<PayOrderChannelResultDto> processCommonCall(CallbackRequest obj) {
        log.info("东方付支付回调对象参数:{}", obj);
        if ("trade_order_status".equals(obj.getNotifyType())) {
            CallbackTradeRequest tradeRequest = JSON.parseObject(DffPayBase64.decode(obj.getBizStr()), CallbackTradeRequest.class);
            log.info("东方付支付回调消息体参数:{}", tradeRequest);
            PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
            PayOrderDO updartePayOrderDO = new PayOrderDO();
            payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
            CommonResult<PayOrderChannelResultDto> commonResult = CommonResult.success(payOrderChannelResultDto);
            updartePayOrderDO.setPayOrderNo(tradeRequest.getOutOrderId());
            if (tradeRequest.getStatus().equals("TRADE_SUCCESS"))
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
            updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(tradeRequest.getPayTime(), DatePattern.NORM_DATETIME_PATTERN));
            updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(tradeRequest.getAmount()));
            updartePayOrderDO.setInsOrderNo(tradeRequest.getOrderId());
            updartePayOrderDO.setChannelOrderNo(tradeRequest.getOrderno());
            return commonResult;
        } else if ("refund_status".equals(obj.getNotifyType())) {
//            CallbackRefundRequest tradeRequest = JSON.parseObject(DffPayBase64.decode(obj.getBizStr()), CallbackRefundRequest.class);
        }
        return CommonResult.success();
    }

    String params(HttpServletRequest request) throws Exception {
        // 读取请求体中的内容
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }
        log.warn("东方付回调返回参数:{}", stringBuilder.toString());
        // 获取完整的 JSON 字符串
        return stringBuilder.toString();
    }
}
